/*
 * 3_server.c
 *
 *  Created on: Apr 29, 2012
 *      Author: root
 */

#include	"unpthread.h"
#include	"readBuffer.h"
#include	"clients.h"

#define USER "230 User logged in\r\n\r\n"
#define TO   "200 OK\r\n\r\n"
#define QUIT "221 service closing\r\n\r\n"
#define OK   "200 OK\r\n\r\n"
#define NOT_LOG "530 Not logged in\r\n\r\n"
#define SYN_ERR "500 Syntax error, command unrecognized\r\n\r\n"
#define DUPLICATE "430 Duplicate ID\r\n\r\n"

Client *client;
pthread_mutex_t clientList_mutex = PTHREAD_MUTEX_INITIALIZER;

int count;
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;

int log_count;
pthread_mutex_t log_count_mutex = PTHREAD_MUTEX_INITIALIZER;

static void *do_server(void *); /* each thread executes this function */

char WHO_part(char *word2);
void FROM_part_all(int sockfd, char *word2, char *word4, char *msg);
void FROM_part_1(int sockfd, char *word2, char *word4, char *msg);
int USER_part(char *word2);
void OUT_display(int sockfd, char *buf);
void FILE_OK_part(char * ID);
void FILE_msg_part(char *ID, char *response);
int SEND_part(int sockfd, int err, char * myid, char *youid, char * response);

int main(int argc, char **argv) {
	int listenfd, *iptr;

	int connfd;
	pthread_t tid;
	socklen_t addrlen, len;
	struct sockaddr *cliaddr;
	struct sockaddr_in *cliaddr2;

	char *IP;
	char ip[20];

	if (argc == 2)
		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
	else if (argc == 3)
		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
	else
		err_quit("usage: tcpserv01 [ <host> ] <service or port>");

	cliaddr = Malloc(addrlen);

	for (;;) {

		len = addrlen;
		iptr = Malloc(sizeof(int));
		connfd = *(int *) iptr;
		connfd = accept(listenfd, cliaddr, &len);

		//////// client's IP address   extraction
		cliaddr2 = cliaddr;
		IP = inet_ntoa(cliaddr2->sin_addr);
		printf("connect : %s\n", IP);

		client = createClient(connfd);

		strcpy(ip, IP);
		printf("\n");
		strcpy(client->ip, ip);

		Pthread_mutex_lock(&count_mutex);
		count++;
		Pthread_mutex_unlock(&count_mutex);
		printf("%d people connect\n", count);
		pthread_create(&tid, NULL, &do_server, client);
	}
}

static void *
do_server(void *cl) {
	Client *client;
	client = cl;

	ssize_t n;
	int num;
	char *token;

	char response[MAXLINE];
	char word1[100], word2[100], word3[100], word4[100], word5[100], word6[100];
	char msg[MAXLINE];
	int err;
	int log_bit;

	Client* user;

	printf("connect %d connfd user !!!!\n", client->connfd);

	pthread_detach(pthread_self());

	for (;;) {
		Pthread_mutex_lock(&clientList_mutex);
		user = findSocket(client->connfd); //find own client 'list' pointer
		Pthread_mutex_unlock(&clientList_mutex);
		if ((n = readClient(user)) < 0)
			err_sys("read error");

		printf("---------\n");

		if (getMessage(user->readBuffer, response, MAXLINE) != NULL) {
			printf("<---");
			fflush(stdout);
			printf(" %d :    ", user->connfd);
			fflush(stdout);
			Fputs(response, stdout);

			num = sscanf(response, "%s %s", word1, word2);

			//confirm login and store client's ID
			if (!strcmp(word1, "USER"))

			{
				err = 1;
				int a = USER_part(word2);
				if (a == 1) //duplicate
						{
					Writen(user->connfd, DUPLICATE, strlen(DUPLICATE));
					//continue;
				} else {
					strcpy(client->user, word2);
					OUT_display(user->connfd, USER);
					Writen(user->connfd, USER, strlen(USER));
					log_bit = 1;

					Pthread_mutex_lock(&log_count_mutex);
					log_count++;
					Pthread_mutex_unlock(&log_count_mutex);
				}
			}
			// normal termination
			if (!strcmp(word1, "QUIT")) {

				OUT_display(user->connfd, QUIT);
				Writen(user->connfd, QUIT, strlen(QUIT));
				printf("disconnected %d user !!!!\n", user->connfd);

				Pthread_mutex_lock(&clientList_mutex);
				destroyClient(user);
				Pthread_mutex_unlock(&clientList_mutex);

				Pthread_mutex_lock(&count_mutex);
				count--;
				Pthread_mutex_unlock(&count_mutex);

				Pthread_mutex_lock(&log_count_mutex);
				log_count--;
				Pthread_mutex_unlock(&log_count_mutex);

				close(user->connfd);
				log_bit = 0;
				break;
			}

			if (log_bit == 1) {	//confirm login bit
				// WHO response message part
				if (!strcmp(word1, "WHO")) {
					err = 1;
					strcpy(word2, user->user);
					WHO_part(word2);
					OUT_display(user->connfd, word2);
					Writen(user->connfd, word2, strlen(word2));
				}
				//parsing FROM message   1:1 or all
				if (!strcmp(word1, "FROM")) {
					num = sscanf(response, "%s %s %s %s %s", word1, word2,
							word3, word4, word5);
					token = strtok(response, "\n");
					token = strtok(NULL, "\r");
					strcpy(msg, token); // msg !!!!

					if (num == 5 && !strcmp(word3, "TO")) { // for exception
						err = 1;

						if (!strcmp(word4, "all")) {

							FROM_part_all(user->connfd, word2, word4, msg);

						} else {
							FROM_part_1(user->connfd, word2, word4, msg);
						}
					}
				}
				// make SEND response message
				if (!strcmp(word1, "SEND")) {

					num = sscanf(response, "%s %s %s %s %s %s", word1, word2,
							word3, word4, word5, word6);
					sprintf(response,
							"298 File transfer attempt\r\nSEND %s FROM %s TO %s\r\n\r\n",
							word2, word4, word6);
					err = SEND_part(user->connfd, err, word4, word6, response);
				}
				// make ACCEPT response message
				if (!strcmp(word1, "ACCEPT")) {
					err = 1;
					num = sscanf(response, "%s %s %s %s %s %s", word1, word2,
							word3, word4, word5, word6);
					sprintf(response,
							"227 %s Ready to receive file\r\nACCEPT %s FROM %s TO %s\r\n\r\n",
							user->ip, word2, word4, word6);
					FILE_msg_part(word4, response);
					FILE_OK_part(word6);
				}

				// make REJECT response message
				if (!strcmp(word1, "REJECT")) {
					err = 1;
					num = sscanf(response, "%s %s %s %s %s %s", word1, word2,
							word3, word4, word5, word6);
					sprintf(response,
							"425 Rejected\r\nREJECT %s FROM %s TO %s\r\n\r\n",
							word2, word4, word6);

					FILE_msg_part(word6, response);
					FILE_OK_part(word4);
				}

			}
			if (err == 0) {
				Writen(user->connfd, SYN_ERR, strlen(SYN_ERR));
			}
			err = 0;
			memset(word1, 0, strlen(word1));
			memset(word2, 0, strlen(word2));
			memset(response, 0, strlen(response));

		}
		// Abnormal termination
		if (n == 0) {
			destroyClient(user);
			Pthread_mutex_lock(&count_mutex);
			count--;
			Pthread_mutex_unlock(&count_mutex);

			Pthread_mutex_lock(&log_count_mutex);
			log_count--;
			Pthread_mutex_unlock(&log_count_mutex);

			log_bit = 0;
			Close(user->connfd);
			printf("********disconnected %d user !!!!\n", user->connfd);
			break;
		}
	}

	return (NULL);
}

// confirm duplicate Id
int USER_part(char *word2) {

	Pthread_mutex_lock(&clientList_mutex);
	for (client = getClientList(); client != NULL; client = client->next) {
		if (!strcmp(client->user, word2)) {
			printf("duplicate ID\n");
			Pthread_mutex_unlock(&clientList_mutex);
			return (1);
		}
	}
	Pthread_mutex_unlock(&clientList_mutex);
	return (0);
}

// who response message detect part
char WHO_part(char *word2) {
	char ID_list[100];
	sprintf(ID_list, "210 OK\r\n");

	Pthread_mutex_lock(&clientList_mutex);
	for (client = getClientList(); client != NULL; client = client->next) {
		strcat(ID_list, " ");
		strcat(ID_list, client->user);

	}
	Pthread_mutex_unlock(&clientList_mutex);
	sprintf(word2, "%s\r\n\r\n", ID_list);
	return (strlen(word2));

}

//all message and error message forwarding
void FROM_part_all(int sockfd, char *word2, char *word4, char *msg) {
	int flag = 0;
	char msg1[MAXLINE];
	strcpy(msg1, msg);

	if (log_count == 1) {
		OUT_display(sockfd, NOT_LOG);
		Writen(sockfd, NOT_LOG, strlen(NOT_LOG));
	}

	else {
		Pthread_mutex_lock(&clientList_mutex);
		for (client = getClientList(); client != NULL; client = client->next) {
			if (strcmp(client->user, ""))
				if (strcmp(client->user, word2)) {
					sprintf(msg,
							"299 Message delivered\r\nFROM %s TO %s\r\n%s\r\n\r\n",
							word2, client->user, msg1);
					OUT_display(client->connfd, msg);
					Writen(client->connfd, msg, strlen(msg));
					if (flag == 0) {
						OUT_display(sockfd, OK);
						Writen(sockfd, OK, strlen(OK));
						flag = 1;
					}
				}
		}
		Pthread_mutex_unlock(&clientList_mutex);
	}
}

// 1:1 message and error message  forwarding
void FROM_part_1(int sockfd, char *word2, char *word4, char *msg) {

	int flag = 0;

	char msg1[MAXLINE];
	strcpy(msg1, msg);

	if (log_count == 1) {
		OUT_display(sockfd, NOT_LOG);
		Writen(sockfd, NOT_LOG, strlen(NOT_LOG));
	} else {
		Pthread_mutex_lock(&clientList_mutex);
		client = findClient(word4);
		Pthread_mutex_unlock(&clientList_mutex);

		if (client != NULL) {
			sprintf(msg, "299 Message delivered\r\nFROM %s TO %s\r\n%s\r\n\r\n",
					word2, word4, msg1);
			printf("parsing\n");
			OUT_display(client->connfd, msg);
			Writen(client->connfd, msg, strlen(msg));

			if (flag == 0) {
				Writen(sockfd, OK, strlen(OK)); //200 OK request message
				flag = 1;
			}

		} else {
			OUT_display(sockfd, NOT_LOG);
			Writen(sockfd, NOT_LOG, strlen(NOT_LOG));

		}
	}
}

//response message display
void OUT_display(int sockfd, char *buf) {
	printf("%d", sockfd);
	fflush(stdout);
	printf("----->");
	fflush(stdout);
	Fputs(buf, stdout);
}


//forwarding SEND message part
int SEND_part(int sockfd, int err, char * myid, char *youid, char * response) {
	Pthread_mutex_lock(&clientList_mutex);
	client = findClient(youid);
	Pthread_mutex_unlock(&clientList_mutex);

	if (!strcmp(myid, youid)) {
		err = 0;

	} else {
		if (client == NULL) {
			err = 1;
			OUT_display(sockfd, NOT_LOG);
			Writen(sockfd, NOT_LOG, strlen(NOT_LOG));
		} else {
			err = 1;
			OUT_display(client->connfd, response);
			Writen(client->connfd, response, strlen(response));
		}

	}
	return (err);
}

//ACCEPT or REJECT 200 OK part
void FILE_OK_part(char * ID) {
	Pthread_mutex_lock(&clientList_mutex);
	client = findClient(ID);
	Pthread_mutex_unlock(&clientList_mutex);

	OUT_display(client->connfd, OK);
	Writen(client->connfd, OK, strlen(OK));
}

//ACCEPT or REJECT  message_part
void FILE_msg_part(char *ID, char *response) {
	Pthread_mutex_lock(&clientList_mutex);
	client = findClient(ID);
	Pthread_mutex_unlock(&clientList_mutex);

	OUT_display(client->connfd, response);
	Writen(client->connfd, response, strlen(response));
}
